package cn.sjtu.security.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.sjtu.security.utils.GlobalConstants;
import cn.sjtu.security.utils.JwtUtils;
import cn.sjtu.security.vo.HttpResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.security.auth.UserPrincipal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * jwt-token过滤器
 */
@Component
@Slf4j
public class JwtCheckFilter extends OncePerRequestFilter {
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${my.tokenPassUrl}")
    private String tokenPassPath;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        // 1. 不用被jwt拦截器拦截的地址
        ArrayList urls = objectMapper.readValue(tokenPassPath, ArrayList.class);

        // 2. 设置相应编码
        httpServletResponse.setContentType("application/json;charset=utf-8");

        // 3. 如果是下面的地址该过滤器直接放行
        String requestURI = httpServletRequest.getRequestURI();
        if(urls.contains(requestURI)) {
            doFilter(httpServletRequest, httpServletResponse, filterChain);
            return;
        }

        // 4. 否则我们需要获取token
        String authorization = httpServletRequest.getHeader("Authorization");

        // 5. 判断字段是否为空
        if(StringUtils.isEmpty(authorization)) {
            HttpResult httpResult = HttpResult.builder().code(0).msg("jwt 为空").build();
            objectMapper.writeValue(httpServletResponse.getOutputStream(), httpResult);
            return;
        }

        // 6. 拿到真正的token
        String jwtToken = authorization.replace("bearer ", "");
        if(StringUtils.isEmpty(jwtToken)) {
            HttpResult httpResult = HttpResult.builder().code(0).msg("jwt 为空").build();
            //httpServletResponse.setContentType("application/json;charset=utf-8");
            objectMapper.writeValue(httpServletResponse.getOutputStream(), httpResult);
            return;
        }

        // 7. 验证token是否有效
        boolean b = jwtUtils.verifyToken(jwtToken);

        // 8. 如果token无效则返回
        if(!b) {
            HttpResult httpResult = HttpResult.builder().code(0).msg("jwt 非法").build();
            //httpServletResponse.setContentType("application/json;charset=utf-8");
            objectMapper.writeValue(httpServletResponse.getOutputStream(), httpResult);
            return;
        }

        // 9. 判断redis中是否存在
        String result = stringRedisTemplate.opsForValue().get(GlobalConstants.LOGIN_TOKEN_PREFIX + jwtToken);

        // 10. 如果不存在则则说明用户已经登出了，这样相当于失效
        if(StrUtil.isBlank(result)) {
            HttpResult httpResult = HttpResult.builder().code(0).msg("jwt 失效").build();
            //httpServletResponse.setContentType("application/json;charset=utf-8");
            objectMapper.writeValue(httpServletResponse.getOutputStream(), httpResult);
            return;
        }

        // 11. 如果token有效则获取token中的认证信息
        Integer userId = jwtUtils.getUserId(jwtToken);
        String username = jwtUtils.getUsername(jwtToken);
        String[] auths = jwtUtils.getAuthsList(jwtToken);

//        List<String> strListAuths = Arrays.stream(auths).collect(Collectors.toList());
//        List<SimpleGrantedAuthority> authorityList = strListAuths.stream().map(item -> {
//            return new SimpleGrantedAuthority(item);
//        }).collect(Collectors.toList());

        // 12. 转换权限对象
        List<SimpleGrantedAuthority> authorityList =
                Arrays.stream(auths).map(item -> new SimpleGrantedAuthority(item)).collect(Collectors.toList());

        // 13. 创建userPrincipal
        UserPrincipal userPrincipal = new UserPrincipal(username);

        // 14. 生成安全框架需要的token
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userPrincipal,null,authorityList);

        // 15. 将token放入安全上下文中,而不使用框架提供的，放入安全上下文就表示验证通过
        SecurityContextHolder.getContext().setAuthentication(token);

        doFilter(httpServletRequest, httpServletResponse, filterChain);
    }
}
